package client;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * The SATClient class defines a program that prompts the user for an input
 * file in CNF format, the host name of the server, the port number, and the 
 * time the client is willing to wait for a solution, and then sends a problem
 * message to the server and prints out the response from the server along with
 * the time it took.
 * 
 * @author Clayton Edward Taylor
 */
public final class SATClient {
	
	static File inputFile;
	static String hostName;
	static int portNumber;
	static int timeOut;

	/**
	 * The entry point for the program defined by SATClient. 
	 * 
	 * @param args - SATClient ignores the command-line arguments
	 * @see          SATClient
	 */
	public static void main(String args[]) {
		
		try {
			readParameters(System.in);
			String problem = readProblem(inputFile);
			Socket server = new Socket(hostName, portNumber);
			long startTime = System.nanoTime();
			sendProblem(problem, timeOut, server.getOutputStream());
			printSolution(server.getInputStream());
			long endTime = System.nanoTime();
			printTimeElapsed(endTime - startTime);
			server.close();
		} catch (IOException e) {
			System.err.println(e.getMessage());
		}
	}

	/**
	 * Prompts the user for the problem definition file, the host name, the
	 * port number, and the maximum time to wait, storing these parameters
	 * in the appropriate static variables.
	 * 
	 * @param is - The InputStream for the user's input
	 * @throws     IOException If an I/O error occurs
	 * @see        SATClient
	 */
	private static void readParameters(InputStream is) throws IOException {
		
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		System.out.println("Please input the following parameters\n");
		System.out.print("Problem definition file: ");
		inputFile = new File(br.readLine());
		System.out.print("Host name: ");
		hostName = br.readLine();
		System.out.print("Port number: ");
		portNumber = Integer.parseInt(br.readLine());
		System.out.print("Maximum time to wait: ");
		timeOut = Integer.parseInt(br.readLine());
		System.out.print("\n");
	}
	
	/**
	 * Reads in a SAT problem definition from a file in CNF format.
	 * 
	 * @param inputFile - The File containing the problem definition
	 * @return            A String holding the entire contents of inputFile
	 * @throws            IOException If an I/O error occurs
	 * @see               SATClient
	 */
	private static String readProblem(File inputFile) throws IOException {
		
		FileReader fr = new FileReader(inputFile);
		BufferedReader br = new BufferedReader(fr);
		StringBuilder sb = new StringBuilder();
		int nextChar;
		while ((nextChar = br.read()) != -1) {
			sb.append((char) nextChar);
		}
		br.close();
				
		return sb.toString();
	}
	
	/**
	 * Creates a SAT problem message from the given problem definition and 
	 * timeout value and sends it to the server.
	 * 
	 * @param problem - A String holding a problem definition
	 * @param timeOut - The maximum amount of time to wait
	 * @param os      - The OutputStream to the server
	 * @see             SATClient
	 */
	private static void sendProblem(String problem, int timeOut, OutputStream os) {
		
		PrintWriter pw = new PrintWriter(os);
		pw.println("sat " + timeOut + " " + problem.length());
		pw.print(problem);
		pw.flush();
	}
	
	/**
	 * Reads the response from the server and prints it out to screen.
	 * 
	 * @param is - The InputStream from the server
	 * @throws     IOException If an I/O error occurs
	 * @see        SATClient
	 */
	private static void printSolution(InputStream is) throws IOException {
		
		InputStreamReader isr = new InputStreamReader(is);
		BufferedReader br = new BufferedReader(isr);
		String line;
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}
	}
	
	/**
	 * Prints out the time elapsed in seconds (with 3 decimal places of
	 * precision).
	 * 
	 * @param elapsedTime - The number of nanoseconds that elapsed
	 * @see                 SATClient
	 */
	private static void printTimeElapsed(long elapsedTime) {
		
		double elapsedSeconds = elapsedTime / 1e9;
		System.out.printf("\nTime Elapsed = %.3f ms\n", elapsedSeconds);
	}
}
